#include <stdlib.h>

#include "masked_image.h"

struct maskedImage {
  pnm* image;
  int** mask;
};

/* Builds a masked image with both pnm image and mask version */
struct maskedImage* initMaskedImage(pnm* ims, pnm* mask) {
  
  struct maskedImage* maskedImg = malloc(sizeof(struct maskedImage));

  maskedImg->image = ims;

  int w = pnm_get_width(*mask);
  int h = pnm_get_height(*mask);

  maskedImg->mask = malloc(h*sizeof(int*));

  for(int i = 0; i < h; i++)
    maskedImg->mask[i] = malloc(w*sizeof(int));
  
  for(int i = 0; i < h; i++) {
    for(int j = 0; j < w; j++) {

      /* Magenta color in mask: masked pixel */
      if(pnm_get_component(*mask,i,j,PnmRed) == 255 && 
	 pnm_get_component(*mask,i,j,PnmGreen) == 0 && 
	 pnm_get_component(*mask,i,j,PnmBlue) == 255)
	
	maskedImg->mask[i][j] = 1;

      else maskedImg->mask[i][j] = 0;	
    }
  }

  return maskedImg;

}

struct maskedImage* initMaskedImageEmpty(int width, int height){

  struct maskedImage* maskedImg = malloc(sizeof(struct maskedImage));

  *maskedImg->image = pnm_new(width, height, PnmRawPpm);

  maskedImg->mask = malloc(height*sizeof(int*));

  for(int i = 0; i < height; i++)
    maskedImg->mask[i] = malloc(width*sizeof(int));
  
  for(int i = 0; i < height; i++) {
    for(int j = 0; j < width; j++) {	
      maskedImg->mask[i][j] = 0;	
    }
  }
  
  return maskedImg;
}

/* Frees a masked image structure */
int freeMaskedImage(struct maskedImage* img) {

  if(img == NULL) return -1;

  int w = pnm_get_width(*img->image);
  int h = pnm_get_height(*img->image);
  
  for(int i = 0; i < h; i++) {
    for(int j = 0; j < w; j++) {
      free(&img->mask[i][j]);      
    }
  }

  for(int i = 0; i < h; i++)
    free(img->mask[i]);

  return 0;
}

/* Counts the number of masked pixels */
int countMaskedPixels(struct maskedImage* img) {

  int count = 0;

  for(int i = 0; i < pnm_get_height(*img->image); i++) {
    for(int j = 0; j < pnm_get_width(*img->image); j++) {

      if(img->mask[i][j] == 1) count++;

    }
  }
  return count;
}

/* Returns a downsampled image (factor 1/2) */
struct maskedImage* downsample(struct maskedImage* img){
  int W = pnm_get_width(*img->image);
  int H = pnm_get_height(*img->image);
  int new_w = W/2;
  int new_h = W/2;

  int kernelEven[] = {1,5,10,10,5,1};
  int kernelOdd[] = {1,4,6,4,1};
  int *kernelx, *kernely;

  if (W%2==0){
    kernelx = malloc(6*sizeof(int));
    kernelx = kernelEven;
  }
  else{
    kernelx = malloc(5*sizeof(int));
    kernelx = kernelOdd;
  }

  if (H%2==0){
    kernely = malloc(6*sizeof(int));
    kernely = kernelEven;
  }
  else{
    kernely = malloc(5*sizeof(int));
    kernely = kernelOdd;
  }

  struct maskedImage* newimage = initMaskedImageEmpty(new_w, new_h);

  for(int y=0,ny=0;y<H-1;y+=2,ny++) {
    for(int x=0,nx=0;x<W-1;x+=2,nx++) {
				
      long r=0,g=0,b=0,ksum=0,masked=0,total=0;
				
      for(int dy=0;dy<sizeof(kernely)/sizeof(int);dy++) {
	int yk=y+dy-2;
	if (yk<0 || yk>=H) continue;
	for(int dx=0;dx<sizeof(kernelx)/sizeof(int);dx++) {
	  int xk = x+dx-2;
	  if (xk<0 || xk>=W) continue;
						
	  total++;
	  if (img->mask[xk][yk]) {masked++;continue;}
						
	  int k = kernelx[dx]*kernely[dy];
	  r+= k*pnm_get_component(*newimage->image, xk, yk, PnmRed);
	  g+= k*pnm_get_component(*newimage->image, xk, yk, PnmGreen);
	  b+= k*pnm_get_component(*newimage->image, xk, yk, PnmBlue);
	  ksum+=k;
	}
      }
				
      if (ksum>0) {
	pnm_set_component(newimage->image, nx, ny, PnmRed, (unsigned short)((double)r/ksum+0.5));
	pnm_set_component(newimage->image, nx, ny, PnmGreen, (unsigned short)((double)g/ksum+0.5));
	pnm_set_component(newimage->image, nx, ny, PnmBlue, (unsigned short)((double)b/ksum+0.5));
	newimage->mask[nx][ny] = 0;
      } else {
	newimage->mask[nx][ny] = 1;
      }
				
				
      if (masked>0.75*total)
	newimage->mask[nx][ny] = 1;
      else
	newimage->mask[nx][ny] = 0;
    }
  }
		
  return newimage;

}
